package middleware

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"jianghairui/uni-upload/appconfig"
	"jianghairui/uni-upload/utils/tools"
	"math"
	"net/http"
	"strconv"
	"strings"
	"time"
)

func Cors() gin.HandlerFunc {

	return func(context *gin.Context) {
		method := context.Request.Method

		context.Header("Access-Control-Allow-Origin", "*")
		context.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token, x-token")
		context.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PATCH, PUT")
		context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
		context.Header("Access-Control-Allow-Credentials", "true")

		if method == "OPTIONS" {
			//context.AbortWithStatus(http.StatusNoContent)
			context.AbortWithStatus(http.StatusOK)
		}
	}

}

func LoggerToFile() gin.HandlerFunc {

	return func(c *gin.Context) {
		// 开始时间
		startTime := time.Now()
		// 处理请求
		c.Next()
		// 结束时间
		// 执行时间
		latencyTime := time.Since(startTime).String()
		// 请求方式
		reqMethod := c.Request.Method
		// 请求路由
		reqUri := c.Request.RequestURI
		// 状态码
		statusCode := c.Writer.Status()
		// 请求IP
		clientIP := c.ClientIP()
		// 日志格式
		appconfig.GlobalLogger.WithFields(logrus.Fields{
			"status_code":  statusCode,
			"latency_time": latencyTime,
			"client_ip":    clientIP,
			"req_method":   reqMethod,
			"req_uri":      reqUri,
		}).Info()
	}
}

func CustomRecovery() gin.HandlerFunc {
	return gin.CustomRecovery(func(ctx *gin.Context, any interface{}) {
		defer func() {
			if err := recover(); err != nil {
				fmt.Println("log failed: ", err)
			}
		}()
		appconfig.GlobalLogger.WithFields(logrus.Fields{
			"status_code": 500,
			"panic_info":  fmt.Sprintf("%s", any),
			"req_uri":     ctx.Request.RequestURI,
		}).Error()
		ctx.String(500, "%s %v", ctx.Request.RequestURI, any)
	})
}

type SignParams struct {
	Timestamp string `json:"timestamp" form:"timestamp" binding:"required"`
	AppKey    string `json:"app_key" form:"app_key"  binding:"required"`
	NonceStr  string `json:"nonce_str" form:"nonce_str"  binding:"required,min=16,max=16"`
}

var signAllow = map[string]bool{
	"/oss/test": true,
}

func CheckLogin(ctx *gin.Context) {
	pathInfo := strings.Split(ctx.Request.URL.String(), "?")
	path := pathInfo[0]
	signCheck, _ := appconfig.AppConfig.Section("").Key("sign_check").Bool()
	if signAllow[path] == false && signCheck == true {
		params := SignParams{}
		err := ctx.ShouldBind(&params)
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "invalid params",
				"data":    err.Error(),
			})
			ctx.Abort()
			return
		}
		timeStamp, err := strconv.ParseInt(params.Timestamp, 10, 64)
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "invalid timestamp ~",
				"data":    err.Error(),
			})
			ctx.Abort()
			return
		}

		timeStampNow := time.Now().Unix()
		if math.Abs(float64(timeStampNow-timeStamp)) > 60*5 {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "invalid timestamp ~~",
				"data":    "",
			})
			ctx.Abort()
			return
		}

		appInfo := appconfig.AppConfig.Section("app_key").KeysHash()
		if appInfo[params.AppKey] == "" {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "invalid app_key",
				"data":    "",
			})
			ctx.Abort()
			return
		}

		appSecret := appInfo[params.AppKey]
		sign, err := tools.SignFromMapString(params, appSecret)
		if err != nil {
			fmt.Println("SignFromMapString error", err)
			return
		}
		postSign := ctx.GetHeader("Authorization")
		if sign != postSign {
			ctx.JSON(200, gin.H{
				"code":    3,
				"message": "签名无效",
				"data":    postSign,
				//"params":    params,
				//"appSecret": appSecret,
			})
			ctx.Abort()
			return
		}
	}

}
